package br.com.intelsys.iflow.driver.struts;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import br.com.intelsys.iflow.IFlowException;

import com.opensymphony.xwork2.Action;

/**
 * Resultado de um signal
 * @author Patrick Givisiez
 *
 */

public class SignalResult {
	/**
	 * Este resultado do signal representa um sucesso.
	 */
	static final boolean RESULT_SUCCESS = true;

	/**
	 * Este resultado do signal representa uma pendencia.
	 */
	static final boolean RESULT_PENDING = false;

	/**
	 * Tipo de acao de resultado (padrao).
	 */
	static final String ACT_TYPE_RESULT = "result";
	
	private List <CfgHandleAction> handleActionList;
	private IFlowInstanceStrutsDriver iFlowInstanceStruts;
	String transitionName;
	private boolean signalResult;
	
	
	private static final Log log = LogFactory.getLog(SignalResult.class);

	SignalResult(IFlowInstanceStrutsDriver iFlowInstanceStruts, String transitionName) {
		this.iFlowInstanceStruts = iFlowInstanceStruts;
		this.transitionName = transitionName;
	}
	
	private void addCfgHandleAction(CfgHandleAction handleAction) {
		if (this.handleActionList == null) {
			this.handleActionList = new ArrayList <CfgHandleAction> ();
		}
		if (signalResult && handleAction instanceof CfgHandleActionSuccess) {
			this.handleActionList.add(handleAction);
			log.debug("Adicionado uma action de sucesso: "+handleAction.getName());
		} else if (!signalResult && handleAction instanceof CfgHandleActionPending) {
			this.handleActionList.add(handleAction);
			log.debug("Adicionado uma action de pendencia: "+handleAction.getName());
		}
	}

	boolean doSignal() throws IFlowException {
		if (signalResult) {
			log.debug("Transicao ok. Processando a transicao: "+transitionName);
			this.iFlowInstanceStruts.startSignal(transitionName);
		} else {
			log.debug("Transicao com pendencias. Negando a transicao: "+transitionName);
			List <CfgHandleAction> actList = this.getHandleActionList();
			if (actList != null) {
				log.debug("Nome das pendencias:");
				for (CfgHandleAction act : actList) {
					log.debug("- "+act.getName()+" (type: "+act.getType()+") value: "+act.getValue()+" / "+act.getPropertyList());
				}
			} else {
				log.error("Sem pendencias listadas! Deveria ter ...");
			}
		}
		return(this.signalResult);
	}
	
	
	/**
	 * Verifica a partir de um IFlowStrutsAction se existe alguma pendencia que evite a propagacao de um <i>signal</i>
	 * @param action Acao do struts
	 * @return Resultado sobre a checagem
	 */

	private void checkTransition(Action action, String transitionName) {
		boolean hasResult = false;
		boolean executeHandle, didHandle = false;
		if (StrutsFlowDriver.getCfgIFlowStruts() != null) {
			log.debug("Procurando pelo processo "+this.iFlowInstanceStruts.getProcessName());
			CfgProcess cfgProcess = StrutsFlowDriver.getCfgIFlowStruts().getCfgProcess(this.iFlowInstanceStruts.getProcessName());
			if (cfgProcess != null) {
				log.debug("Processo encontrado. Procurando pela transition "+transitionName);
				CfgTransition cfgTransition = cfgProcess.getCfgTransition(transitionName);
				if (cfgTransition != null) {
					List <String> pendingList;
					if (cfgTransition.getTransitionValidator() != null) {
						log.debug("Verificando pendencias por um validator: "+cfgTransition.getTransitionValidator().getClass());
						pendingList = cfgTransition.getTransitionValidator().validate(this.iFlowInstanceStruts.getTransition(transitionName));
					} else if (action != null && action instanceof IFlowStrutsAction){
						log.debug("Verificando pendencias por um IFLowStrutsAction: "+action.getClass());
						pendingList = ((IFlowStrutsAction)action).getPendingList();
					} else {
						if (action != null) {
							log.debug("Foi informado uma action mas ela nao eh uma IFLowStrutsAction.");
						}
						log.debug("Sem verificacoes de pendencias. Nenhum TransitionValidator ou IFlowStrutsAction encontrado.");
						pendingList = null;
					}
					
					if (pendingList != null && !pendingList.isEmpty()) {
						log.debug("Existem pendencias. Checar os handles");
					} else {
						log.debug("Sem pendencia. Procurando a acao de sucesso.");
					}
					List <CfgHandle> handleList = cfgTransition.getHandleList();
					for (CfgHandle cfgHandle : handleList) {
						List <String> fromList = cfgHandle.getFromList();
						executeHandle = true;
						if (fromList != null) {
							for (String from : fromList) {
								if (from != null) {
									if (!from.equals(iFlowInstanceStruts.getStateName())) {
										executeHandle = false;
									}
								}
							}
						}
						if (executeHandle) {
							didHandle = true;
							List <CfgHandleAction> cfgHandleAction = cfgHandle.getHandleActionList();
							if (pendingList != null && !pendingList.isEmpty()) {
								for (String pending : pendingList) {
									log.debug("Procurando actions para "+pending);
									for (CfgHandleAction act : cfgHandleAction) {
										log.debug("Action encontrada: "+act.getName());
										if (act instanceof CfgHandleActionPending) {
											log.debug("Action eh instancia de CfgHandleActionPending");
											if (act.getName() != null && act.getName().equals(pending)) {
												if (!hasResult) {
													log.debug("Criado um SignalResult com pendencia.");
													signalResult = SignalResult.RESULT_PENDING;
													hasResult = true;
												}
												log.debug("Action adicionada: "+act.getName());
												addCfgHandleAction(act);
											}
										}
									}
								}
							} else {
								for (CfgHandleAction act : cfgHandleAction) {
									log.debug("Action encontrada: "+act.getName());
									if (act instanceof CfgHandleActionSuccess) {
										log.debug("Action eh instancia de CfgHandleActionPending");
										log.debug("Criado um SignalResult com sucesso.");
										hasResult = true;
										signalResult = SignalResult.RESULT_SUCCESS;
										log.debug("Action adicionada: "+act.getName());
										if (act.getValue() == null || act.getValue().length() == 0) {
											log.debug("Action de sucesso sem valor, definindo o valor padrao de sucesso do struts: "+Action.SUCCESS);
											act.setValue(Action.SUCCESS);
										}
										addCfgHandleAction(act);
										break;
									}
								}
							}
						}
						if (hasResult) {
							break;
						}
					}
					if (!didHandle) {
						log.debug("Nenhum handle encontrado para o config da transicao: "+cfgTransition.getNameList());
					}
				} else {
					log.debug("Nenhuma configuracao de transicao encontrada para: "+transitionName);
				}
			} else {
				log.error("Nenhum processo encontrado para este workflow.");
			}
		} else {
			log.error("Nenhuma configuracao encontrada para as transicoes via Struts.");
		}
		if (!hasResult) {
			log.debug("Nenhuma action encontrada. Criando um SignalResult de sucesso sem action.");
			signalResult = SignalResult.RESULT_SUCCESS;
		}
	}

	/**
	 * Recupera o resultado de um signal sem validacao da Action. Util quando usa-se o validator direto na <transition>
	 * 
	 * @return Resultado da acao para o Struts
	 * @throws IFlowException 
	 */
	public String getStrutsResult() throws IFlowException {
		return(this.getStrutsResult((Action)null));
	}
	
	public String getStrutsResult(IFlowStrutsAction action) throws IFlowException {
		return(this.getStrutsResult((Action)action));
	}

	/**
	 * Recupera o resultado de um signal com validacao pela action (IFlowStrutsAction)
	 * 
	 * @param action Action do Struts para colher informacoes
	 * @return Resultado da acao para o Struts
	 * @throws IFlowException 
	 */
	public String getStrutsResult(Action action) throws IFlowException {
		String ret = null;
		Class c = null;
		Method[] mList = null;
		List <CfgProperty> cfgPropertyList;
		StringBuilder sb = new StringBuilder();
		String fieldName;
		String callMethodFormated;
		final String SET_TMP = "set";
		boolean fieldDone = false;
		this.checkTransition(action, this.transitionName);
		this.doSignal();
		if (this.handleActionList != null) {
			for (CfgHandleAction act : this.handleActionList) {
				log.debug("Processando a action: "+act.getName()+" - "+act.getClass().getName());
				if ((signalResult && act instanceof CfgHandleActionSuccess) || (!signalResult && act instanceof CfgHandleActionPending)) {
					if (act.getType() == null || act.getType().length() == 0 || act.getType().equals(ACT_TYPE_RESULT)) {
						// Acao do tipo padrao "resultado"
						cfgPropertyList = act.getPropertyList();
						if (cfgPropertyList != null) {
							if (action != null) {
								for (CfgProperty prop : cfgPropertyList) {
									fieldName = prop.getName();
									if (fieldName != null && fieldName.length() > 0) {
										fieldDone = false;
										if (c == null) {
											c = action.getClass();
											mList = c.getMethods();
										}
										sb = new StringBuilder();
										sb.append(SET_TMP);
										sb.append(Character.toUpperCase(fieldName.charAt(0)));
										sb.append(fieldName.substring(1, fieldName.length()));
										callMethodFormated = sb.toString();
										for (Method m : mList) {
											if (callMethodFormated.equals(m.getName())) {
												log.debug("Definindo a propriedade '"+callMethodFormated+"' com o valor: "+prop.getValue());
												try {
													m.invoke(action, prop.getValue());
													fieldDone = true;
												} catch (IllegalArgumentException e) {
													e.printStackTrace();
												} catch (IllegalAccessException e) {
													e.printStackTrace();
												} catch (InvocationTargetException e) {
													e.printStackTrace();
												}
											}
										}
										if (!fieldDone) {
											log.error("A propriedade '"+fieldName+"' nao foi definida. Provavelmente nao a encontrei na action.");
										}
									} else {
										log.error("Nome da propriedade invalido: "+fieldName);
									}
								}
							} else {
								log.warn("Foi solicitado a definicao de propriedades mas nao foi informado uma action.");
							}
						}
						ret = act.getValue();
						break;
					}
				}
			}
		} else {
			ret = null;
		}
		return (ret);
	}
	
	List <CfgHandleAction> getHandleActionList() {
		return(handleActionList);
	}
}
